﻿using System;
using System.Linq;
using nRoute.Utilities;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Threading;
using nRoute.Components.Collections;
#if (!SILVERLIGHT)
using System.Collections.Concurrent;
#endif

namespace nRoute.Components.Composition
{
    internal sealed class ResourceLocatorCatalog
        : ConcurrentDictionary<string, IResourceLocator>, INotifyCollectionChanged, IDisposable
    {

#region Declarations

        private const string RESOURCE_NOTFOUND = "Resource type '{0}' not found";
        private const string RESOURCE_WITHKEY_NOTFOUND = "Resource type '{0}', with key '{1}' not found";
        private const int INITIAL_CAPACITY = 101;
        private const int CONCCURRENCY_LEVEL_FACTOR = 2;

        private readonly Type _resourceType;
        private IResourceCatalog _resourceCatalog;

        private volatile bool _isKnownResource;
        private volatile IResourceLocator _defaultResourceLocator;

        public event NotifyCollectionChangedEventHandler CollectionChanged;
        
#endregion

        public ResourceLocatorCatalog(Type resourceType, IResourceCatalog resourceCatalog)
           : base(Environment.ProcessorCount * CONCCURRENCY_LEVEL_FACTOR, INITIAL_CAPACITY, Compatability.INVARIANT_IGNORECASE_STRING_COMPARER) 
        {
            Guard.ArgumentNotNull(resourceType, "resourceType");
            Guard.ArgumentNotNull(resourceCatalog, "resourceCatalog");
            
            _resourceType = resourceType;
            _resourceCatalog = resourceCatalog;
        }

#region Properties

        public bool IsKnownResource
        {
            get { return _isKnownResource; }
        }

#endregion

#region Methods

        public bool RegisterResource(string key, IResourceLocator resourceLocator)
        {
            if (base.TryAdd(key, resourceLocator))
            {
                // set the default 
                if (_defaultResourceLocator == null)
                {
                    Interlocked.Exchange<IResourceLocator>(ref _defaultResourceLocator, resourceLocator);
                }

                var _collectionChangedHandler = this.CollectionChanged;
                if (_collectionChangedHandler != null)
                {
                    _collectionChangedHandler(_resourceCatalog, 
                        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, resourceLocator, -1));
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        public void RegisterDefaultResourceLocator(string key)
        {
            var _resourceLocator = ResolveResourceLocator(key, true);
            if (_defaultResourceLocator != _resourceLocator) Interlocked.Exchange<IResourceLocator>(ref _defaultResourceLocator, _resourceLocator);
        }

        public bool UnregisterResource(string resourceName)
        {
            var _resourceLocator = default(IResourceLocator);
           if (base.TryRemove(resourceName, out _resourceLocator))
            {
                // check if the default resource
                Interlocked.CompareExchange<IResourceLocator>(ref _defaultResourceLocator, _resourceLocator, default(IResourceLocator));
                _resourceLocator.Dispose();

                var _collectionChangedHandler = this.CollectionChanged;
                if (_collectionChangedHandler != null)
                {
                    _collectionChangedHandler(_resourceCatalog, 
                        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, _resourceLocator, -1));
                }

                // if there is no default, use the first one
                if (_defaultResourceLocator == default(IResourceLocator))
                {
                    var _list = base.GetEnumerator();
                    if (_list.MoveNext())
                    {
                        Interlocked.Exchange<IResourceLocator>(ref _defaultResourceLocator, _list.Current.Value);
                    }
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        public IResourceLocator ResolveDefaultResourceLocator(bool throwIfNotFound)
        {
            var _resourceLocator = _defaultResourceLocator;
            if (_resourceLocator != null)
            {
                return _resourceLocator;
            }
            else if (throwIfNotFound)
            {
                throw new KeyNotFoundException(string.Format(RESOURCE_NOTFOUND, _resourceType.FullName));
            }

            return null;
        }

        public IResourceLocator ResolveResourceLocator(string key, bool throwIfNotFound)
        {
            var _resourceLocator = default(IResourceLocator);
            if (base.TryGetValue(key, out _resourceLocator))
            {
                return _resourceLocator;
            }
            else if (throwIfNotFound)
            {
                throw new KeyNotFoundException(string.Format(RESOURCE_WITHKEY_NOTFOUND, _resourceType.FullName, key));
            }

            return null;
        }

        public void RegisterAsKnownType()
        {
            if (!_isKnownResource) _isKnownResource = true;
        }

#endregion

#region Helper

        private void Dispose(bool disposing) 
        {
            if (disposing)
            {
                var _collectionChangedHandler = this.CollectionChanged;
                if (_collectionChangedHandler != null)
                {
                    _collectionChangedHandler(_resourceCatalog, 
                        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
                }
                _collectionChangedHandler = null;
                _resourceCatalog = null;

                Interlocked.Exchange<IResourceLocator>(ref _defaultResourceLocator, default(IResourceLocator));
            }
        }

#endregion

#region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

#endregion

        ~ResourceLocatorCatalog() { Dispose(false); }

    }
}